package jmine.tec.batch.domain;

import static jmine.tec.persist.schema.api.IndexOrder.ASC;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import jmine.tec.persist.annotation.Alias;
import jmine.tec.persist.annotation.Comment;
import jmine.tec.persist.annotation.Comments;
import jmine.tec.persist.annotation.Constraint;
import jmine.tec.persist.annotation.DiscriminatorComment;
import jmine.tec.persist.annotation.Index;
import jmine.tec.persist.annotation.Indexes;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.validator.NotNull;

import bancosys.tec.component.Documentation;
import bancosys.tec.persist.bussobj.PersistableBusinessObject;
import bancosys.tec.persist.persister.annotation.NaturalKey;
import bancosys.tec.persist.persister.annotation.SystemId;

// TODO [Frank] Criar constraint unica no mnemonico.
/**
 * Representa a meta informação da fase.
 */
@Entity
@Table(name = "MPHASE")
@Alias("MPHASE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.INTEGER)
@Comment(table = "MPHASE", column = "DISCRIMINATOR", value = "IDENTIFICADOR DA FASE.")
@Constraint(suffix = "0")
@DiscriminatorValue(value = "0")
@DiscriminatorComment("FASE PADRAO")
@SequenceGenerator(name = "SEQ_MPHASE", sequenceName = "SEQ_MPHASE")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Documentation("ARMAZENA AS FASES QUE COMPOEM O PROCESSO EXECUTADO PELA CARTEIRA. TABELA PREENCHIDA VIA POST SCRIPT."
        + "NAO HA CADASTRO NO SISTEMA PARA INCLUSAO/ALTERACAO DOS SEUS DADOS.")
public class MPhase extends PersistableBusinessObject {
    private Long id;

    private String mnemonico;

    private String descr;

    private String beanName;

    private Collection<MProcess> processes = new ArrayList<MProcess>();

    private Set<MPhase> dependencies = new HashSet<MPhase>();

    private List<PhaseInstance<?>> phaseInstances = new ArrayList<PhaseInstance<?>>();

    /**
     * @return o valor do id.
     */
    @Id
    @SystemId
    @Column(name = "COD_MPHASE")
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_MPHASE")
    @Documentation("CODIGO DA FASE DO PROCESSO.")
    public Long getId() {
        return this.id;
    }

    /**
     * Seta o valor do Id.
     * 
     * @param id o valor do id.
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * O mnemonico da fase.
     * 
     * @return o mnemonico da fase.
     */
    @NotNull
    @Column(name = "MNE", length = 45, nullable = false)
    @Documentation("MNEMONICO DA FASE.")
    @Index(suffix = "0")
    @NaturalKey
    public String getMnemonico() {
        return this.mnemonico;
    }

    /**
     * Seta o mnemonico da fase.
     * 
     * @param mnemonico o valor do mnemonico.
     */
    public void setMnemonico(String mnemonico) {
        this.mnemonico = mnemonico;
    }

    /**
     * @return a descrição da fase.
     */
    @Column(name = "DESCR", length = 110)
    @Documentation("DESCRICAO DA FASE.")
    public String getDescr() {
        return this.descr;
    }

    /**
     * Seta a descriçao da fase.
     * 
     * @param descr a descriçao da fase.
     */
    public void setDescr(String descr) {
        this.descr = descr;
    }

    /**
     * O nome do bean no spring.
     * 
     * @return o nome dpo bean.
     */
    @Column(name = "BEAN", length = 110)
    @Documentation("BEAN QUE REPRESENTA A EXECUÇÃO DA MPHASE.")
    public String getBeanName() {
        return this.beanName;
    }

    /**
     * O nome do bean no spring.
     * 
     * @param beanName o nome do bean.
     */
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    /**
     * Os processos que contém essa fase.
     * 
     * @return os processos que contém essa fase.
     */
    @ManyToMany(mappedBy = "phases", fetch = FetchType.LAZY)
    public Collection<MProcess> getProcesses() {
        return this.processes;
    }

    /**
     * Seta os processos que contém essa fase.
     * 
     * @param processes a coleção de processos que contém essa fase.
     */
    public void setProcesses(Collection<MProcess> processes) {
        this.processes = processes;
    }

    /**
     * Adiciona um process para essa fase.
     * 
     * @param process o processo.
     */
    public void addProcess(MProcess process) {
        this.processes.add(process);
    }

    /**
     * As fases de que essa fase depende para ser executada.
     * 
     * @return as fases de que essa fase depende.
     */
    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "PHASE_DEPENDENCIES", joinColumns = { @JoinColumn(name = "COD_MPHASE", nullable = false, updatable = false) }
    , inverseJoinColumns = { @JoinColumn(name = "COD_MPHASE_DEPENDS", nullable = false, updatable = false) })
    @Alias("PHDEPE")
    @ForeignKey(name = "FK_MPHASE_PHDEPE", inverseName = "FK_MPHASE_PHDEPE_1")
    @Comments({ @Comment(table = "PHASE_DEPENDENCIES", value = "ARMAZENA AS DEPENDENCIAS ENTRE AS FASES DO PROCESSO."),
            @Comment(table = "PHASE_DEPENDENCIES", column = "COD_MPHASE", value = "CODIGO DA FASE DO PROCESSO."),
            @Comment(table = "PHASE_DEPENDENCIES", column = "COD_MPHASE_DEPENDS", value = "CODIGO DA FASE DEPENDENTE.") })
    @Indexes({ @Index(columns = "COD_MPHASE_DEPENDS", suffix = "0", orders = ASC),
            @Index(columns = "COD_MPHASE", suffix = "1", orders = ASC) })
    public Set<MPhase> getDependencies() {
        return this.dependencies;
    }

    /**
     * Seta as fases que essa fase depende.
     * 
     * @param dependencies A coleção de fases que essa fase depende.
     */
    public void setDependencies(Set<MPhase> dependencies) {
        this.dependencies = dependencies;
    }

    /**
     * Adiciona um dependencia dessa fase. <br>
     * Não verifica se a dependência já existe!
     * 
     * @param other a fase da qual essa depende.
     */
    public void addDependency(MPhase other) {
        this.dependencies.add(other);
    }

    /**
     * Verifica que a fase faz parte do processo.
     * 
     * @param process o processo.
     * @return TRUE se a fase faz parte do processo.
     */
    public boolean containProcess(MProcess process) {
        return this.getProcesses().contains(process);
    }

    /**
     * toString da fase.
     * 
     * @return a string da fase, mostrando o ID e o MNEMONICO.
     */
    @Override
    public String toString() {
        return "[" + this.getId() + "] " + this.getMnemonico();
    }

    /**
     * @return the phaseInstances
     */
    @OneToMany(mappedBy = "phase")
    protected List<PhaseInstance<?>> getPhaseInstances() {
        return this.phaseInstances;
    }

    /**
     * @param phaseInstances the phaseInstances to set
     */
    protected void setPhaseInstances(List<PhaseInstance<?>> phaseInstances) {
        this.phaseInstances = phaseInstances;
    }
}
